import os
import hashlib
import json
import logging
import configparser
from datetime import datetime
from fastapi import FastAPI, File, UploadFile, HTTPException, Body
from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles

# 创建FastAPI应用
app = FastAPI(
    title="云打印系统API",
    description="用于云打印系统的后端API服务",
    version="1.0.0"
)

# 配置CORS
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],  # 生产环境中应该限制来源
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# 设置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler("logs/api.log", encoding='utf-8'),
        logging.StreamHandler()
    ]
)

# 确保日志目录存在
os.makedirs("logs", exist_ok=True)

# 配置静态文件
app.mount("/static", StaticFiles(directory="static"), name="static")

@app.post("/api/upload")
async def upload_file(file: UploadFile = File(...)):
    """接收并处理上传的文件"""
    try:
        # 获取上传文件信息
        content_type = file.content_type
        filename = file.filename
        file_extension = os.path.splitext(filename)[1].lower()
        
        # 检查文件类型
        config = load_config()
        supported_extensions = config.get('supported_extensions', ['.pdf', '.doc', '.docx','.xls','.xlsx','.ppt','.pptx','.txt','.jpg','.jpeg','.png','.gif','.bmp','.tiff','.ico','.webp'])
        
        if file_extension not in supported_extensions:
            logging.warning(f"不支持的文件类型: {filename}, 类型: {content_type}")
            raise HTTPException(
                status_code=400, 
                detail=f"不支持的文件类型: {file_extension}. 支持的类型: {', '.join(supported_extensions)}"
            )
        
        # 确保目标文件夹存在
        watch_folder = config.get('watch_folder', './watch_folder')
        os.makedirs(watch_folder, exist_ok=True)
        
        # 使用时间戳创建唯一文件名
        timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
        unique_filename = f"{os.path.splitext(filename)[0]}_{timestamp}{file_extension}"
        file_path = os.path.join(watch_folder, unique_filename)
        
        # 保存文件
        with open(file_path, "wb") as buffer:
            buffer.write(await file.read())
        
        file_size = os.path.getsize(file_path)
        logging.info(f"文件上传成功: {unique_filename}, 大小: {file_size} 字节")
        
        # 生成文件哈希值（用于防止重复打印）
        file_hash = calculate_file_hash(file_path)
        
        # 检查是否为重复文件
        is_duplicate = check_file_duplicate(file_hash)
        
        return {
            "status": "success",
            "message": "文件上传成功，已加入打印队列",
            "filename": unique_filename,
            "original_filename": filename,
            "file_size": file_size,
            "file_hash": file_hash,
            "is_duplicate": is_duplicate,
            "time": datetime.now().isoformat()
        }
    except HTTPException as e:
        raise e
    except Exception as e:
        logging.error(f"文件上传失败: {str(e)}")
        raise HTTPException(status_code=500, detail=f"文件上传失败: {str(e)}")

def calculate_file_hash(filepath):
    """计算文件的MD5哈希值"""
    try:
        hasher = hashlib.md5()
        with open(filepath, 'rb') as f:
            buf = f.read(65536)  # 读取64kb块
            while len(buf) > 0:
                hasher.update(buf)
                buf = f.read(65536)
        return hasher.hexdigest()
    except Exception as e:
        logging.error(f"计算文件哈希值失败: {str(e)}")
        return None

def check_file_duplicate(file_hash):
    """检查文件是否为重复文件"""
    try:
        if file_hash is None:
            return False
            
        # 检查文件哈希记录
        hash_file = "print_status/file_hashes.json"
        if os.path.exists(hash_file):
            with open(hash_file, 'r', encoding='utf-8') as f:
                file_hashes = json.load(f)
                if str(file_hash) in file_hashes:
                    return True
        return False
    except Exception as e:
        logging.error(f"检查文件重复性失败: {str(e)}")
        return False

def load_config():
    """加载配置文件"""
    config = {}
    try:
        config_parser = configparser.ConfigParser()
        if os.path.exists('config.ini'):
            config_parser.read('config.ini', encoding='utf-8')
            
            if 'Settings' in config_parser:
                supported_extensions = config_parser.get('Settings', 'supported_extensions', 
                                                      fallback='.pdf,.doc,.docx').split(',')
                watch_folder = config_parser.get('Settings', 'watch_folder', fallback='./watch_folder')
                default_printer = config_parser.get('Settings', 'default_printer', fallback='')
                
                config['supported_extensions'] = [ext.strip().lower() for ext in supported_extensions]
                config['watch_folder'] = watch_folder
                config['default_printer'] = default_printer
        else:
            # 使用默认配置
            config['supported_extensions'] = ['.pdf', '.doc', '.docx']
            config['watch_folder'] = './watch_folder'
            config['default_printer'] = ''
            logging.warning("配置文件不存在，使用默认配置")
    except Exception as e:
        logging.error(f"加载配置失败: {str(e)}")
        # 使用默认配置
        config['supported_extensions'] = ['.pdf', '.doc', '.docx']
        config['watch_folder'] = './watch_folder'
        config['default_printer'] = ''
    
    return config

@app.get("/")
def read_root():
    """返回API根路径信息"""
    return {
        "app": "云打印系统API",
        "version": "1.0.0",
        "status": "运行中",
        "time": datetime.now().isoformat()
    }

@app.get("/api/health")
def health_check():
    """健康检查API"""
    return {
        "status": "ok",
        "time": datetime.now().isoformat(),
        "service": "cloud-printing-api",
        "version": "1.0.0"
    }

@app.get("/api/queue")
def print_queue():
    """获取打印队列状态"""
    try:
        # 检查打印状态文件
        queue_status = {
            "processed_count": 0,
            "printed_count": 0,
            "queue_files": [],
            "recent_prints": []
        }
        
        # 读取已处理文件数
        processed_file = "print_status/processed_files.json"
        if os.path.exists(processed_file):
            with open(processed_file, 'r', encoding='utf-8') as f:
                processed_data = json.load(f)
                processed_files = processed_data.get('files', [])
                queue_status["processed_count"] = len(processed_files)
        
        # 读取已打印文件数
        printed_file = "print_status/printed_files.json"
        if os.path.exists(printed_file):
            with open(printed_file, 'r', encoding='utf-8') as f:
                printed_data = json.load(f)
                printed_files = printed_data.get('files', [])
                queue_status["printed_count"] = len(printed_files)
        
        # 读取文件哈希记录，包含最近打印的文件
        hash_file = "print_status/file_hashes.json"
        if os.path.exists(hash_file):
            with open(hash_file, 'r', encoding='utf-8') as f:
                hash_data = json.load(f)
                # 将哈希记录转换为列表，按时间排序
                hash_list = []
                for hash_key, hash_info in hash_data.items():
                    if isinstance(hash_info, dict) and 'time' in hash_info and 'filename' in hash_info:
                        hash_list.append({
                            'hash': hash_key,
                            'filename': hash_info.get('filename', '未知文件'),
                            'time': hash_info.get('time', ''),
                            'filepath': hash_info.get('filepath', '')
                        })
                
                # 按时间排序，最新的在前
                hash_list.sort(key=lambda x: x.get('time', ''), reverse=True)
                queue_status["recent_prints"] = hash_list[:10]  # 只显示最近的10个
        
        # 读取监控文件夹内等待打印的文件
        config = load_config()
        watch_folder = config.get('watch_folder', './watch_folder')
        if os.path.exists(watch_folder):
            queue_files = []
            for filename in os.listdir(watch_folder):
                file_path = os.path.join(watch_folder, filename)
                if os.path.isfile(file_path):
                    file_stat = os.stat(file_path)
                    queue_files.append({
                        'filename': filename,
                        'size': file_stat.st_size,
                        'created_time': datetime.fromtimestamp(file_stat.st_ctime).isoformat(),
                        'status': '等待打印'
                    })
            queue_status["queue_files"] = queue_files
        
        return queue_status
    except Exception as e:
        logging.error(f"获取打印队列状态失败: {str(e)}")
        raise HTTPException(status_code=500, detail=f"获取打印队列状态失败: {str(e)}")

@app.post("/api/print")
async def print_file(request: dict = Body(...)):
    """接收并处理打印请求"""
    try:
        # 获取文件名
        filename = request.get('filename')
        if not filename:
            raise HTTPException(
                status_code=400,
                detail="缺少文件名参数"
            )
        
        # 检查文件是否存在
        config = load_config()
        watch_folder = config.get('watch_folder', './watch_folder')
        file_path = os.path.join(watch_folder, filename)
        
        if not os.path.exists(file_path):
            raise HTTPException(
                status_code=404,
                detail=f"文件不存在: {filename}"
            )
        
        # 调用打印函数
        success = send_to_printer(file_path)
        
        if not success:
            raise HTTPException(
                status_code=500,
                detail="打印失败，请检查打印机状态"
            )
        
        # 记录打印信息
        try:
            record_print_history(file_path)
        except Exception as e:
            logging.warning(f"记录打印历史失败: {str(e)}")
        
        # 不再删除文件
        # try:
        #     os.remove(file_path)
        #     logging.info(f"文件已打印并删除: {filename}")
        # except Exception as e:
        #     logging.warning(f"删除文件失败: {str(e)}")
        
        logging.info(f"文件已打印: {filename}")
        
        return {
            "status": "success",
            "message": "文件已发送到打印机",
            "filename": filename,
            "time": datetime.now().isoformat()
        }
    except HTTPException as e:
        raise e
    except Exception as e:
        logging.error(f"打印过程出错: {str(e)}")
        raise HTTPException(status_code=500, detail=f"打印失败: {str(e)}")

def send_to_printer(file_path):
    """发送文件到打印机，尝试多种方式，并添加详细错误处理"""
    try:
        import win32print
        import win32api
        import tempfile
        import os
        import subprocess
        import time
        from datetime import datetime
        
        filename = os.path.basename(file_path)
        
        # 获取打印机名称
        config = load_config()
        printer_name = config.get('default_printer', '')
        if not printer_name:
            printer_name = win32print.GetDefaultPrinter()
            
        # 检查打印机是否可用
        try:
            # 列出所有打印机
            printers = [printer[2] for printer in win32print.EnumPrinters(win32print.PRINTER_ENUM_LOCAL | win32print.PRINTER_ENUM_CONNECTIONS)]
            logging.info(f"系统中的打印机: {printers}")
            
            if printer_name not in printers:
                logging.error(f"指定的打印机 '{printer_name}' 不存在")
                return False
                
            # 检查打印机状态
            printer_handle = win32print.OpenPrinter(printer_name)
            if not printer_handle:
                logging.error(f"无法打开打印机 '{printer_name}'")
                return False
                
            printer_info = win32print.GetPrinter(printer_handle, 2)
            win32print.ClosePrinter(printer_handle)
            
            if printer_info['Status'] != 0:
                logging.error(f"打印机状态异常: {printer_info['Status']}")
                return False
        except Exception as e:
            logging.error(f"检查打印机时出错: {str(e)}")
            # 继续尝试打印，因为某些情况下即使这里报错，打印依然可能成功
        
        # 记录打印信息
        logging.info(f"开始打印文件: {filename}")
        logging.info(f"打印机: {printer_name}")
        logging.info(f"文件路径: {file_path}")
        logging.info(f"文件大小: {os.path.getsize(file_path)} 字节")
        
        # 文件扩展名
        _, ext = os.path.splitext(file_path)
        ext = ext.lower()
        
        # 尝试方法1: 使用printto命令
        try:
            logging.info(f"尝试方法1: 使用ShellExecute printto命令")
            result = win32api.ShellExecute(
                0, 
                "printto", 
                file_path,
                f'"{printer_name}"', 
                ".", 
                0
            )
            
            if result > 32:
                logging.info(f"方法1成功: printto命令已发送")
                time.sleep(1)  # 给系统一些时间处理打印命令
                return True
            else:
                logging.error(f"方法1失败: printto命令错误码 {result}")
        except Exception as e:
            logging.error(f"方法1异常: {str(e)}")
        
        # 尝试方法2: 使用默认打印程序
        try:
            logging.info(f"尝试方法2: 使用默认打印程序")
            result = win32api.ShellExecute(
                0, 
                "print", 
                file_path,
                None,
                ".", 
                0
            )
            
            if result > 32:
                logging.info(f"方法2成功: 默认打印程序已启动")
                time.sleep(1)
                return True
            else:
                logging.error(f"方法2失败: 默认打印程序错误码 {result}")
        except Exception as e:
            logging.error(f"方法2异常: {str(e)}")
        
        # 尝试方法3: 使用命令行打印
        try:
            if ext in ['.txt', '.log', '.csv']:
                logging.info(f"尝试方法3: 使用命令行工具打印文本文件")
                cmd = f'copy "{file_path}" "{printer_name}"'
                logging.info(f"执行命令: {cmd}")
                result = subprocess.run(cmd, shell=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
                
                if result.returncode == 0:
                    logging.info(f"方法3成功: 命令行打印完成")
                    return True
                else:
                    logging.error(f"方法3失败: 命令行打印错误 {result.stderr.decode('gbk', errors='ignore')}")
        except Exception as e:
            logging.error(f"方法3异常: {str(e)}")
            
        # 所有方法都失败了
        logging.error(f"所有打印方法均失败，无法打印文件: {filename}")
        return False
    except Exception as e:
        logging.error(f"打印过程出错: {str(e)}")
        return False

def record_print_history(file_path):
    """记录打印历史"""
    try:
        # 确保记录目录存在
        os.makedirs("print_status", exist_ok=True)
        
        # 计算文件哈希值
        file_hash = calculate_file_hash(file_path)
        if file_hash is None:
            return
        
        filename = os.path.basename(file_path)
        hash_file = "print_status/file_hashes.json"
        
        # 读取现有记录
        file_hashes = {}
        if os.path.exists(hash_file):
            with open(hash_file, 'r', encoding='utf-8') as f:
                file_hashes = json.load(f)
        
        # 添加新记录
        file_hashes[str(file_hash)] = {
            'filepath': file_path,
            'filename': filename,
            'time': datetime.now().isoformat()
        }
        
        # 保存记录
        with open(hash_file, 'w', encoding='utf-8') as f:
            json.dump(file_hashes, f, ensure_ascii=False, indent=2)
            
        logging.info(f"已记录打印历史: {filename}")
    except Exception as e:
        logging.error(f"记录打印历史失败: {str(e)}")
        raise e

@app.get("/api/test-printer")
def test_printer():
    """测试打印机连接和功能"""
    try:
        import win32print
        import tempfile
        import os
        
        # 获取打印机名称
        config = load_config()
        printer_name = config.get('default_printer', '')
        if not printer_name:
            printer_name = win32print.GetDefaultPrinter()
        
        # 创建临时测试文件
        temp_dir = tempfile.gettempdir()
        test_file = os.path.join(temp_dir, "print_test.txt")
        
        with open(test_file, "w") as f:
            f.write("云打印系统测试页\n")
            f.write("="*40 + "\n")
            f.write(f"时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
            f.write(f"打印机: {printer_name}\n")
            f.write("="*40 + "\n")
            f.write("如果您看到这个页面，表示打印系统工作正常。\n")
        
        # 打印测试页
        success = send_to_printer(test_file)
        
        if success:
            return {
                "status": "success",
                "message": "测试页已发送到打印机",
                "printer": printer_name,
                "time": datetime.now().isoformat()
            }
        else:
            return {
                "status": "error",
                "message": "测试页打印失败，请检查打印机状态",
                "printer": printer_name,
                "time": datetime.now().isoformat()
            }
    except Exception as e:
        logging.error(f"测试打印失败: {str(e)}")
        raise HTTPException(status_code=500, detail=f"测试打印失败: {str(e)}")

@app.get("/api/printers")
def get_printers():
    """获取系统中所有打印机列表"""
    try:
        import win32print
        
        # 获取当前配置中的默认打印机
        config = load_config()
        default_printer = config.get('default_printer', '')
        
        # 获取系统默认打印机
        system_default = win32print.GetDefaultPrinter()
        
        # 获取所有打印机
        printers = []
        for printer in win32print.EnumPrinters(win32print.PRINTER_ENUM_LOCAL | win32print.PRINTER_ENUM_CONNECTIONS):
            printer_name = printer[2]
            printers.append({
                'name': printer_name,
                'is_default': (printer_name == system_default),
                'is_selected': (printer_name == default_printer) if default_printer else (printer_name == system_default)
            })
        
        return {
            "status": "success",
            "printers": printers,
            "system_default": system_default,
            "selected_default": default_printer if default_printer else system_default
        }
    except Exception as e:
        logging.error(f"获取打印机列表失败: {str(e)}")
        raise HTTPException(status_code=500, detail=f"获取打印机列表失败: {str(e)}")

@app.post("/api/set-default-printer")
async def set_default_printer(request: dict = Body(...)):
    """设置默认打印机"""
    try:
        printer_name = request.get('printer_name')
        if not printer_name:
            raise HTTPException(status_code=400, detail="缺少打印机名称参数")
        
        # 验证打印机是否存在
        import win32print
        printers = [printer[2] for printer in win32print.EnumPrinters(win32print.PRINTER_ENUM_LOCAL | win32print.PRINTER_ENUM_CONNECTIONS)]
        if printer_name not in printers:
            raise HTTPException(status_code=400, detail=f"打印机 '{printer_name}' 不存在")
        
        # 更新配置文件
        config_parser = configparser.ConfigParser()
        if os.path.exists('config.ini'):
            config_parser.read('config.ini', encoding='utf-8')
            
            if 'Settings' not in config_parser:
                config_parser.add_section('Settings')
            
            config_parser.set('Settings', 'default_printer', printer_name)
            
            with open('config.ini', 'w', encoding='utf-8') as f:
                config_parser.write(f)
            
            logging.info(f"默认打印机已更新为: {printer_name}")
            
            return {
                "status": "success",
                "message": f"默认打印机已设置为 {printer_name}",
                "printer": printer_name
            }
        else:
            raise HTTPException(status_code=500, detail="配置文件不存在")
    except HTTPException as e:
        raise e
    except Exception as e:
        logging.error(f"设置默认打印机失败: {str(e)}")
        raise HTTPException(status_code=500, detail=f"设置默认打印机失败: {str(e)}")

if __name__ == "__main__":
    import uvicorn
    # 确保打印状态目录存在
    os.makedirs("print_status", exist_ok=True)
    # 确保静态文件目录存在
    os.makedirs("static", exist_ok=True)
    # 确保监控文件夹存在
    config = load_config()
    watch_folder = config.get('watch_folder', './watch_folder')
    os.makedirs(watch_folder, exist_ok=True)
    
    # 读取服务配置
    try:
        config_parser = configparser.ConfigParser()
        port = 8000
        host = "0.0.0.0"
        log_level = "info"
        
        if os.path.exists('config.ini'):
            config_parser.read('config.ini', encoding='utf-8')
            if 'Service' in config_parser:
                port = config_parser.getint('Service', 'port', fallback=8000)
                host = config_parser.get('Service', 'host', fallback="0.0.0.0")
                log_level = config_parser.get('Service', 'log_level', fallback="info")
    except Exception as e:
        logging.error(f"读取服务配置失败: {str(e)}")
    
    # 启动服务
    logging.info(f"启动FastAPI服务器 {host}:{port}")
    uvicorn.run("app:app", host=host, port=port, log_level=log_level) 